package concurrecy.lock3;


import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantLock;

class JannaReentrantLock extends JannaLock {

    public JannaReentrantLock() {
        super(new Sync());
    }

    private static class Sync extends AbstractQueuedSynchronizer {
        @Override
        protected boolean tryAcquire(int arg) {
            int s = getState();
            if(s == 0) {
                if(compareAndSetState(0, arg)) {
                    setExclusiveOwnerThread(Thread.currentThread());
                    return true;
                }
            } else {
                if(getExclusiveOwnerThread() == Thread.currentThread()) {
                    setState(s + arg);
                    return true;
                }
            }
            return false;
        }

        @Override
        protected boolean tryRelease(int arg) {
            if(getExclusiveOwnerThread() != Thread.currentThread())
                throw new IllegalMonitorStateException();
            boolean release = false;
            int s = getState();
            int next = s - 1;
            if(next == 0) {
                release = true;
                setExclusiveOwnerThread(null);
            }
            setState(next);
            return release;
        }
    }


}

class LockChecker implements Runnable {

    private Lock lock;

    public LockChecker(Lock lock) {
        this.lock = lock;
    }

    @Override
    public String toString() {
        return Thread.currentThread().getName();
    }

    @Override
    public void run() {
        System.out.println(this + " 开始抢锁");
        lock.lock();
        System.out.println(this + " 抢到锁");
        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            System.out.println(this + " sleep interrupted");
        } finally {
            lock.unlock();
            System.out.println(this + " 释放锁");
        }
    }
}

public class MyLockDemo {
    /**
     * 测试多线程获取锁
     * @param lock
     */
    static void test2(Lock lock) {
        ExecutorService service = Executors.newCachedThreadPool();
        service.execute(new LockChecker(lock));
        service.execute(new LockChecker(lock));
        service.execute(new LockChecker(lock));
        service.execute(new LockChecker(lock));
        service.shutdown();
    }

    /**
     * 测试多线程竞争
     */
    static void test3(Lock lock, int n) throws InterruptedException {
        class Data {
            int x = 0;
        }
        final Data data = new Data();
        ExecutorService executorService = Executors.newCachedThreadPool();
        CountDownLatch latch = new CountDownLatch(n);
        long point = System.currentTimeMillis();
        for (int i = 0; i < n; i++) {
            executorService.execute(() -> {
                for (int j = 0; j < 100_000; j++) {
                    lock.lock();
                    try {
                        lock.lock();
                        data.x ++;
                        lock.unlock();
                    } finally {
                        lock.unlock();
                    }
                }
                latch.countDown();
            });
        }
        latch.await();
        System.out.println("use " + (System.currentTimeMillis() - point));
        executorService.shutdown();
        System.out.println(data.x);
    }

    /**
     * 测试中断
     */
    static void test4(Lock lock) throws InterruptedException {
        Thread thread = new Thread(() -> {
            System.out.println("thread ready ... ");
            try {
                lock.lockInterruptibly();
            } catch (InterruptedException e) {
                System.out.println("lock interrupted");
            }
        });
        lock.lock();
        try {
            thread.start();
            System.out.println("main sleep 1000");
            Thread.sleep(1000);
            System.out.println("interrupt thread");
            thread.interrupt();
        } finally {
            lock.unlock();
        }
    }

    /**
     * 测试超时
     */
    static void test5(Lock lock) {
        Thread thread = new Thread(() -> {
            System.out.println("thread ready ... ");
            try {
                boolean s = lock.tryLock(2000, TimeUnit.MILLISECONDS);
                System.out.println(s);
            } catch (InterruptedException e) {
                System.out.println("lock interrupted");
            }
        });
        lock.lock();
        try {
            thread.start();
            System.out.println("main sleep 3000");
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Lock lock = new JannaReentrantLock();
//        Lock lock = new ReentrantLock();
//        ReadWriteLock readWriteLock = new JannaReentrantReadWrite();
//        Lock lock = readWriteLock.writeLock();
//        Lock lock = readWriteLock.readLock();
//        test2(lock);
        test3(lock, 8);
//        test4(lock);
//        test5(lock);
    }
}
